

#include <klib.h>
#include <memory.h>
#include <kernel.h>


unsigned int __memory;
struct __page_directory_entry *__pdir_table =
  (struct __page_directory_entry *) (__KERNEL_PAGES - (1 << 12));

unsigned int __pdir_table_len;
unsigned int __generated_tables = 0;

struct __page_table_entry *__page_table = 
       (struct __page_table_entry *) __KERNEL_PAGES;

/**
 * Code to generate the page tables used by freeGSD. There is a page table
 * to hold directory entries which point to page tables that have entries
 * defining actual pages.
 */

void __print_pdir_entry(struct __page_directory_entry table){

  char buf[33];

  k_print("  Page dir entry:\n");

  itobs(table.flags, buf);
  k_print("    Flags: "); k_print(&buf[25]);
  k_printl();
  itox(table.base_addr << 12, buf, 9);
  k_print("    Base addr of page table: "); k_print(buf);
  k_printl();

}

unsigned int written_dirs = 0;

void __make_kernel_page_dir(){

  /* First calculate how many 4 MByte blocks of mem we have. */
  unsigned int real_mem = __memory + 1024; /* __memory starts at 1 meg. */

  int blocks = real_mem / 4096;
  unsigned int addr = __KERNEL_PAGES;

  /* For each of the blocks, make a pdir entry. */
  int i;
  for (i = 0; i < blocks; i++){
    __write_page_dir_entry((unsigned int) i, addr, 
			   __PG_PRESENT | __PG_READ_WRITE);
    addr += 4096;
    if (written_dirs++ < 3){
      __print_pdir_entry(__pdir_table[i]);
    }
  }

  __pdir_table_len = blocks;

}

void __write_page_dir_entry(unsigned int index, 
			    unsigned int base_addr, char flags){

  if (index > 1023) return; // Too big an index.

  __pdir_table[index].flags     = flags;
  __pdir_table[index].res       = 0;
  __pdir_table[index].base_addr = (base_addr >> 12);

}

/**
 * base_addr: the address of the *first page*. The 1024 that follow
 * will be moved 4096 bytes up in memory as needed.
 * flags: flags for the entries.
 * dir: the 
 */
void __make_kernel_page_table(char flags, void *base_addr, 
			      struct __page_directory_entry dir){
  
  int i = 0;
  while ( i < 1024){
    
    struct __page_table_entry tmp;
    tmp.flags = flags;
    tmp.global = 0;
    tmp.res = 0;
    tmp.base_addr = ((unsigned int) base_addr >> 12);

    struct __page_table_entry *pdir = 
      (struct __page_table_entry *)(dir.base_addr << 12);
    pdir[i] = tmp;

    base_addr += 4096;
    __generated_tables++;
    i++;
  }

}

void __print_page_table_entry(struct __page_table_entry p){

  char buf[33];
  k_print("  Page table entry:\n");

  itobs(p.flags, buf);
  k_print("    Flags: "); k_print(&buf[25]); k_printl();

  itox(p.base_addr << 12, buf, 9);
  k_print("    Page addr: "); k_print(buf), k_printl();

}

void init_kernel_mem(){

  char str[16];

  /* First make the page directory table. */
  __make_kernel_page_dir();
  k_print("  Kernel page dir length: ");
  itos((int) __pdir_table_len, str, 16);
  k_print(str);
  k_printl();
  k_print("  Writing page tables...\n");

  unsigned int i = 0;
  for ( ; i < __pdir_table_len; i++){
    __make_kernel_page_table(__PG_PRESENT | __PG_READ_WRITE, 
			     (void *)(0x400000 * i), __pdir_table[i]);
  }

  itos((int) __generated_tables, str, 16);
  k_print("  Checking generated tables: ");
  k_print(str);
  k_printl();

  __print_page_table_entry(__page_table[0]);
  __print_page_table_entry(__page_table[1]);

  unsigned int p = __use_pages((unsigned int) __pdir_table);
  itox((unsigned int) __pdir_table, str, 9);
  k_print(str); k_printl();
  itox(p, str, 9);
  k_print(str); k_printl();

}
